-
Notifications
You must be signed in to change notification settings - Fork 143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature Proposal: Rasterization of SVGs at Runtime for Eclipse Icons #1638
base: master
Are you sure you want to change the base?
Feature Proposal: Rasterization of SVGs at Runtime for Eclipse Icons #1638
Conversation
We could clean up (most of) these references across bundles so that we can delete (most of) the PNGs. |
bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/SVGRasterizer.java
Outdated
Show resolved
Hide resolved
Thank you @Michael5601 for this PR I'm really looking forward to this. Withouth having it tried out yet, I have a few remarks/questions/suggestions, all based on the assumption that providing support to render vector graphics is an 'implementation' detail that does not need any or only little new APIs where technically necessary (e.g. to specify a zoom factor). Therefore I wonder for example a new public API type Splitting the steps to handle all kind of icons sounds very reasonable for me. I think this should just focus on adding support for rendering vector graphics, just as it's done.
It's correct that Fragments can't have own Fragments, but a (host) bundle can have an arbitrary number of fragments. Setting up the java ServiceLoader between OSGi bundles (not fragments) on the other hand is a bit more complicated, but also possible. Just for reference, this blog post should explain it (I don't want to go into the details here): |
Test Results 382 files - 1 382 suites - 1 6m 13s ⏱️ + 1m 20s For more details on these failures, see this check. Results for commit 44e6f19. ± Comparison against base commit 2a61cb2. This pull request removes 35 tests.
This pull request skips 1 and un-skips 4 tests.
♻️ This comment has been updated with latest results. |
Thank you for the comment, I will check all your remarks soon.
To this point I can already say that I tried it with the same article but I wasn't successful. |
10b80f3
to
3612a63
Compare
That's something we should discuss and agree on. I would not in favor of tightly integrating a specific SVG rasterizer into SWT. Having it interchangeable ensures low coupling and gives the flexibility to exchange it without rebuilding SWT (by just providing some other fragment/bundle/... that contains an alternative rasterizer implementation). We could still think of making that interface internal for now, so that the interface is not public but in case you would want to provide a different rasterizer it would still be possible (with potentially a warning).
For some more clarifiation on using a fragment: If we are not mistaken, to provide a fragment with a concrete rasterizer implementation, the rasterizer interface needs to be placed directly in the SWT host bundle, not in some common package that is mapped into all the native fragments like done for all the other SWT code. Otherwise, the SVG fragment would be tied to the presence of another fragment providing that interface, which (reasonably) seems to be prohibited. As a consequence, that interface would become the first source element to be placed directly in the SWT host bundle, while all the other code is placed in the native fragments. In addition, we need to reference that interface from within the native fragments (to make use a rasterizer in SWT code). With the current configuration of the bundles/fragment, this is not possible. The SWT host bundle has to be added to the classpath of the native fragments to access the interface via adding the PDE required plugins container to its classpath (i.e., to add the classpath entry
I just want to add that I also tried to create a minimal reproducer for using a ServiceLoader across OSGi bundles, but I also failed, just like Michael. The necessary capabilities added to the manifests do not even show up when listing the capabilities of the bundles via the OSGi console. Probably, I am / we are doing something wrong here. |
3612a63
to
e40e2a8
Compare
I edited the PR message and added the following points:
|
bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ISVGRasterizer.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/SVGRasterizer.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ISVGRasterizer.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGRasterizerRegistry.java
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
Outdated
Show resolved
Hide resolved
Feature Proposal: Rasterization of SVGs at Runtime for Eclipse Icons Fixes eclipse-platform#1438 Eclipse currently loads icons exclusively as raster graphics (e.g., `.png`), without support for vector formats like `.svg`. A major drawback of raster graphics is their inability to scale without degrading image quality. Additionally, generating icons of different sizes requires manually rasterizing SVGs outside Eclipse, leading to unnecessary effort and many icon files. This PR introduces support for vector graphics in Eclipse, enabling SVGs to be used for icons. Existing PNG icons will continue to be loaded alongside SVGs, allowing the use of the new functionality without the need to replace all PNG files at once. --- - **How It Works**: - To use SVG icons, simply place the SVG file in the bundle and reference it in the `plugin.xml` and other necessary locations, as is done for PNGs. No additional configuration is required. - At runtime, Eclipse uses the library JSVG to rasterize the SVG into a raster image of the desired size, eliminating the need for scaling. My analysis shows that JSVG is the most suitable Java library for this purpose. - You need to write the flag `-Dswt.autoScale=quarter` into your `eclipse.ini` file or into the run arguments of a new configuration.
e40e2a8
to
0266fb9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should follow the java recommendation for interface names here the I
prefix is more an eclipse thing and even there we do not follow this anymore and I don't have seen it in SWT aynwhere
bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ISVGRasterizer.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/SVGRasterizer.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the stream handling is flawed at the moment and reading SVG documents should be done using streams or URLs.
bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ISVGRasterizer.java
Outdated
Show resolved
Hide resolved
e6bd186
to
ffa17e4
Compare
The caller needs to make sure the method is called with an SVG file. The check now only checks the first byte.
3cb61c5
to
3bc3621
Compare
Please see this Draft for the Follow-up-functionality for the automatic customization of graphically customized icons as announced previously in this PR. The functionality is complete but the draft is meant as a base for discussion. All changes to this PR will be done also in the draft. |
Yes, absolutely. I suggest we discuss this in a separate issue, in parallel to this PR.
Besides adding the correct required and provided capabilities to the provider and consumer of the service (implementation), you also have to make sure that Everyone building products or launching Eclipse-apps from a workspace that include this SVG support for SWT would have to configure this. This would IMHO be cumbersome and to avoid all these difficulties I strongly would like to avoid the need for the OSGI Service Loader mediator in this case.
That's right. The only disadvantage I can think of for having code in the But I tried a few things out and had another idea:
Yet another option would be to avoid a new Of course this would more or less make it impossible to supply alternative rasterizer. But I cannot tell if it's realistic that custom ones will be supplied or not and we are just over-engineering here? |
Export-Package: org.eclipse.swt.svg | ||
Import-Package: org.eclipse.swt.graphics |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Export-Package: org.eclipse.swt.svg | |
Import-Package: org.eclipse.swt.graphics | |
Fragment-Host: org.eclipse.swt | |
Export-Package: org.eclipse.swt.svg |
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/> | ||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> | ||
<classpathentry kind="src" path="src"/> | ||
<classpathentry kind="lib" path="libs/jsvg-1.6.1.jar"/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<classpathentry kind="lib" path="libs/jsvg-1.6.1.jar"/> | |
<classpathentry combineaccessrules="false" kind="src" path="/org.eclipse.swt.win32.win32.x86_64" /> | |
<classpathentry kind="lib" path="libs/jsvg-1.6.1.jar"/> |
This makes this project on o.e.swt.win32.win32.x86_64 and adds the latter to the classpath of the former, similar like how it's happening at runtime.
Of course this now only works for win32.x86_64. I also added other swt-fragments, but it looks like this is not optional and on a first sight I didn't found a way to make it optional. But it is possible to turn incomplete-classpath errors into warnings by adding org.eclipse.jdt.core.incompleteClasspath=warning
to .settings/org.eclipse.jdt.core.prefs
.
And to make it work in the build it is hopefully sufficient to add a pom.xml to the project where, depending on the running platform, an explicit dependency to the corresponding native fragment is added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to admit that I am not very experienced with this topic. It seems you have an idea how this could work with fragments if I read it right. This would be very nice. It would be great if we could have a call where you can explain this approach to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just pushed the changes made for/in our 1:1 call yesterday. Feel free to in-cooperate/squash them into your other commits.
I also added a few todos of things that could be done further.
bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGRasterizer.java
Show resolved
Hide resolved
bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
Outdated
Show resolved
Hide resolved
This is not true, all fragments are merged with the host and therefore you have one big class space, it might be that the IDE (or Tycho) currently have some limitations in this regard but we could/should fix that instead of making things harder than required. A |
6c16f65
to
b5365d8
Compare
I agree. I created this discussion for this topic. |
I just realised that cocoa and gtk not only have different implementation for |
and add the other swt-fragments containing the native code explicitly to the build-path of swt.svg. Because the classloader of the host is shared by all fragments, the plain Java ServiceLoader can now be used to get an implementation of SVGRasterizer.
Eclipse currently loads icons exclusively as raster graphics (e.g.,
.png
), without support for vector formats like.svg
. A major drawback of raster graphics is their inability to scale without degrading image quality. Additionally, generating icons of different sizes requires manually rasterizing SVGs outside Eclipse, leading to unnecessary effort and many icon files.This PR introduces support for vector graphics in Eclipse, enabling SVGs to be used for icons. Existing PNG icons will continue to be loaded alongside SVGs, allowing the use of the new functionality without the need to replace all PNG files at once.
Key Features
Screenshots showcasing the new functionality can be found below. These screenshots were taken with 125% monitor-zoom and scaling enabled with the flag
-Dswt.autoScale=quarter
.How It Works:
plugin.xml
and other necessary locations, as is done for PNGs. No additional configuration is required.-Dswt.autoScale=quarter
into youreclipse.ini
file or into the run arguments of a new configuration.Demonstration:
This PR includes SVG versions of icons for the following bundles:
org.eclipse.search
org.eclipse.ui
org.eclipse.ui.ide
org.eclipse.ui.ide.application
org.eclipse.ui.editors
org.eclipse.ui.navigator
Due to this the PR of Platform UI is very large. If preferred, I can limit the changes to
org.eclipse.search
, allowing SVG icons to be tested specifically in the search result window.I did not delete any exisiting PNGs.
Architecture
Eclipse icons fall into three categories, all three adressed by this PR:
Standard Icons:
These is the most common type of icons. They are loaded, scaled if needed, and displayed in the UI.
Composite Icons:
Composite icons combine a base icon with up to four overlay icons. Each component is loaded separately and then combined for display.
Graphically Customized Icons:
These icons represent disabled or "grayed-out" states. They can either be provided as pre-designed raster images or generated dynamically at runtime by applying transformations to the "enabled" version of the icon. Eclipse does not have no pre-designed SVGs. As soon as the path of the pre-designed raster graphic is removed the icon will be created at runtime.
Standard-Icons and Composite Icons are fully supported by this PR. Graphically Customized Icons are only supported if the pre-designed raster graphic is removed. The original image loaded before the customization, is generated by rasterizing a SVG. Afterwards the current automatic customization functionality creates the "grayed-out" style. This currenct functionality for customization produces bad results as seen here. This is why I want to change this behaviour in a future PR.
The solution of my future PR applies a runtime filter to SVGs before rasterization (pre-processing), which can emulate GTK or Cocoa (macOS)/Windows styles. Specifically, GTK uses a unique style for disabled icons and I can only produce results that fit to either GTK or Windows and Cocoa. I will release this feature as a follow up to this PR as soon as a solution is found for the different styles for different OS. As discussion for this topic can be found here. Alternatively I can release the PR before and we live with the other visuals for one OS. For now I recommend to use the pre-designed PNGs as usual. The future PR for the customization of icons with SVGs is nearly complete but requires further refinement before release.
The sequence diagrams below illustrate the architecture for loading Standard and Composite Icons. The new functionality introduced in this PR is highlighted in blue. The new functionality changes the ImageLoader process on the right side of the diagramms. You can see that the implementation can happen in the same place for these two icon groups:
Sequence-Diagram: Standard-Icons (Currenct Workflow)
Sequence-Diagram: Standard-Icons (Improved Workflow)
Sequence-Diagram: Composite-Icons (Currenct Workflow. See also the two little referenced workflows below)
Sequence-Diagram: Composite-Icons (Improved Workflow)
For Graphically Customized Icons, only the current workflow will be shown in a sequence diagram here. The new workflow as described above will be part of the follow up PR. You can see that by calling
URLImageDescriptor.createImage()
the Standard-Icons workflow is called.Sequence-Diagram: Disabled-Icons (Current Workflow)
Dependency Management
To enable SVG rasterization, the library JSVG is required as an external dependency. Since all rasterization logic needs to be part of Eclipse SWT, the dependency must also be included in SWT. However, SWT currently has no external dependencies, which created challenges for the integration of the Dependency:
Unsuccessful Approaches:
Fragment Approach:
Adding a fragment to SWT with the JSVG dependency failed, as SWT itself is composed of fragments, and fragments cannot depend on other fragments.
Service Loader Approach:
Adding a new bundle with the functionality and the dependency failed because SWT fragments lack a class loader to locate the new bundle. (I am not sure if this reason is correct but I tried for some hours and it did not work)
Successful Approach:
I created a new bundle containing the rasterization logic and the JSVG dependency. This bundle registers itself via a hook mechanism during initialization. To ensure the bundle is loaded, I added an initialization line to
Workbench.createAndRunWorkbench(Display, WorkbenchAdvisor)
.Once JSVG is included in the target platform (with help from @HannesWell), this functionality will become fully operational.
Outstanding Issues
Target Platform:
The JSVG library must be included in the target platform.
Existing PNGs:
PNG files cannot yet be deleted, as some are referenced across bundles via hardcoded paths e.g. in platform code. Removing these files would result in errors.
Early-Loaded Icons:
A few icons (e.g.,
eclipse16.png
) are loaded before the workbench is initialized. At this point, the rasterizer has not yet been registered. I only experienced three icons that were loaded this early but there might be more.Scaling Support for Composite Icons:
I modified the behavior of the
CompositeImageDescriptor.supportsZoomLevel(int zoom)
method in Platform UI to address a limitation where it only allowed zoom levels that were exact multiples of 100 (e.g., 100, 200, 300). This restriction, caused by an unresolved bug, prevented scaling to zoom levels such as 125. Although I haven't analyzed the underlying bug yet, this change was necessary to enable proper scaling functionality.Missing or broken SVGs
There are some icons that don't have a SVG-File or the SVG-File needs to be improved. The feature can still be used as there will be raster graphics that are loaded instead but finally this issue needs to be fixed. I will provide PRs if I find these icons.
Planned Tasks for this PR
Regression Testing:
I will add regression tests that allow automatic testing of the feature.
Performance Evaluation:
I will perform performance tests to find out if this feature is faster or slower than the current implementation. From manual testing I can say that it feels rather the same.
Testing for different OS
The
ImageLoader
class has specific implementations for cocoa, win32 and gtk that I needed to change. All three implementations are very similar, so I don't think there will be an issue but the cocoa and gtk implementation was no yet tested by me. I only tested on a windows system.See also this PR for the necessary changes in Platform UI. All changes for this feature were performed in SWT and Platform UI.
Fixes #1438.
Let me know if you'd like further clarification or additional adjustments!